#include "precomp.h"

namespace Tmpl {

// ===========================================================
// General stuff
// ===========================================================
	
struct WidgetListItem
{
	WidgetListItem(Widget* w, int s) : widget(w), spacing(s) {}
	Widget* widget;
	int spacing;
};
typedef std::vector<WidgetListItem> WidgetList;

static void sOrderWidgets(int x, int y, int w, int h,
						  WidgetList& list, bool horizontal,
						  WidgetSet::Order orderH, WidgetSet::Order orderV)
{
	const int widgetCount = (int)list.size();
	if(widgetCount == 0) return;

	// Gather size information
	int maxW=0, maxH=0, totalW=0, totalH=0;
	WidgetList::iterator it;
	for(it = list.begin(); it != list.end(); ++it)
	{
		if(it->widget)
		{
			const Vec2i size = it->widget->GetSize();
			maxW = Max(maxW, size.x), totalW += size.x;
			maxH = Max(maxH, size.y), totalH += size.y;
		}
		int ws = 0, hs = it->spacing;
		if(horizontal) Swap(ws, hs);
		totalW += ws, totalH += hs;
	}

	// Set new widget positions
	Vec2i pos(x,y), delta(0,0);
	if(!horizontal)
	{
		if(orderV == WidgetSet::BOTTOM) pos.y = y + h - totalH;
		if(orderV == WidgetSet::CENTER) pos.y = y + (h - totalH)/2;
		if(orderV == WidgetSet::SPREAD) delta.y = (h - totalH) / Max(widgetCount-1, 1);
		for(it = list.begin(); it != list.end(); ++it)
		{
			Widget* wg = it->widget;
			if(wg)
			{
				Vec2i wgSize = wg->GetSize(), wgPos = wg->GetPos();
				if(orderH == WidgetSet::UNORDERED) pos.x = wgPos.x;
				if(orderV == WidgetSet::UNORDERED) pos.y = wgPos.y;

				if(orderH == WidgetSet::RIGHT) pos.x = x + w - wgSize.x;
				if(orderH == WidgetSet::CENTER) pos.x = x + (w - wgSize.x)/2;
				if(orderH == WidgetSet::SPREAD) wgSize.x = w;
			
				wg->SetPos(pos.x, pos.y);
				wg->SetSize(wgSize.x, wgSize.y);
				pos.y += wgSize.y + delta.y;
			}
			else pos.y += it->spacing;
		}
	}
	else
	{
		if(orderH == WidgetSet::RIGHT) pos.x = x + w - totalW;
		if(orderH == WidgetSet::CENTER) pos.x = x + (w - totalW)/2;
		if(orderH == WidgetSet::SPREAD) delta.x = (w - totalW) / Max(widgetCount-1, 1);
		for(it = list.begin(); it != list.end(); ++it)
		{
			Widget* wg = it->widget;
			if(wg)
			{
				Vec2i wgSize = wg->GetSize(), wgPos = wg->GetPos();
				if(orderH == WidgetSet::UNORDERED) pos.x = wgPos.x;
				if(orderV == WidgetSet::UNORDERED) pos.y = wgPos.y;

				if(orderV == WidgetSet::BOTTOM) pos.y = y + h - wgSize.y;
				if(orderV == WidgetSet::CENTER) pos.y = y + (h - wgSize.y)/2;
				if(orderV == WidgetSet::SPREAD) wgSize.y = h;
				
				wg->SetPos(pos.x, pos.y);
				wg->SetSize(wgSize.x, wgSize.y);
				pos.x += wgSize.x + delta.x;
			}
			else pos.x += it->spacing;
		}
	}
}

// ===========================================================
// WidgetSet implementation
// ===========================================================

struct WidgetSetData
{
	WidgetList list;
};

WidgetSet::WidgetSet(const char* label, int w, int h, int x, int y) : Widget(label, w, h, x, y)
{
	myData = new WidgetSetData;
}

void WidgetSet::SetPos(int x, int y)
{
	myX = x, myY = y;
	Refit();
}
void WidgetSet::SetSize(int w, int h)
{
	myW = w, myH = h;
	Refit();
}

void WidgetSet::Tick(float deltaTime)
{
	WidgetList::iterator it;
	for(it = myData->list.begin(); it != myData->list.end(); ++it)
	{
		if(it->widget) it->widget->Tick(deltaTime);
	}
}

void WidgetSet::Draw()
{
	WidgetList::iterator it;
	for(it = myData->list.begin(); it != myData->list.end(); ++it)
	{
		if(it->widget) it->widget->Draw();
	}
}

// ===========================================================
// OrdenedSet implementation
// ===========================================================

OrdenedSet::OrdenedSet(const char* label, int w, int h, int x, int y) : 
	WidgetSet(label, w, h, x, y), myOrderH(UNORDERED), myOrderV(UNORDERED), myIsHorizontal(false)
{
}

void OrdenedSet::SetOrdering(bool horizontal, Order orderH, Order orderV)
{
	myIsHorizontal = horizontal;
	myOrderH = orderH, myOrderV = orderV;
	Refit();
}

void OrdenedSet::TightFit()
{
	// Gather size information
	int maxW=0, maxH=0, totalW=0, totalH=0;
	WidgetList::iterator it;
	for(it = myData->list.begin(); it != myData->list.end(); ++it)
	{
		if(it->widget)
		{
			const Vec2i size = it->widget->GetSize();
			maxW = Max(maxW, size.x), totalW += size.x;
			maxH = Max(maxH, size.y), totalH += size.y;
		}
		int ws = 0, hs = it->spacing; if(myIsHorizontal) Swap(ws, hs);
		totalW += ws, totalH += hs;
	}

	// Refit
	myW = myIsHorizontal ? totalW : maxW;
	myH = myIsHorizontal ? maxH : totalH;
	Refit();
}

void OrdenedSet::Refit()
{
	sOrderWidgets(myX, myY, myW, myH, myData->list, myIsHorizontal, myOrderH, myOrderV);
}

void OrdenedSet::AddWidget(Widget* widget)
{
	myData->list.push_back(WidgetListItem(widget,0));
	Refit();
}

void OrdenedSet::AddSpacing(int space)
{
	myData->list.push_back(WidgetListItem(0,space));
	Refit();
}

// ===========================================================
// LabeledSet implementation
// ===========================================================

LabeledSet::LabeledSet(const char* label, int w, int h, int x, int y, bool collapsable) : 
	OrdenedSet(label, w, h, x, y), myIsCollapsed(false), myIsCollapsable(collapsable)
{
}

void LabeledSet::Refit()
{
	const int charH = TmplFont.GetHeight();
	myX += 6, myY += 6+charH, myW -= 12, myH -= 12+charH;
	OrdenedSet::Refit();
	myX -= 6, myY -= 6+charH, myW += 12, myH += 12+charH;
}

void LabeledSet::TightFit()
{
	const int charH = TmplFont.GetHeight();
	myX += 6, myY += 6+charH, myW -= 12, myH -= 12+charH;
	OrdenedSet::TightFit();
	myX -= 6, myY -= 6+charH, myW += 12, myH += 12+charH;
}

void LabeledSet::Tick(float deltaTime)
{
	WidgetSet::Tick(deltaTime);
}

void LabeledSet::Draw()
{
	Draw::SetBlendMode(Draw::ALPHA);
	Draw::Bar(myX+4, myY+4, myX+myW-4, myY+myH-4, 0x22000000);
	Draw::SetBlendMode(Draw::NORMAL);

	WidgetSet::Draw();

	const int textW = TmplFont.GetWidth(myLabel)/2 + 4;
	Draw::LineH(myX+3, myY+4, myX+myW/2-textW, myStyle.cBorder);
	Draw::LineH(myX+myW/2+textW, myY+4, myX+myW-4, myStyle.cBorder);
	Draw::LineH(myX+3, myY+myH-4, myX+myW-4, myStyle.cBorder);
	Draw::LineV(myX+4, myY+4, myY+myH-4, myStyle.cBorder);
	Draw::LineV(myX+myW-4, myY+4, myY+myH-4, myStyle.cBorder);
	
	TmplFont.Print(myLabel, myX+myW/2, myY+2, myStyle.cText, Font::CENTER);
}

// ===========================================================
// Tabs implementation
// ===========================================================

struct TabsItemImp
{
	TabsItemImp(Widget* wdg, const char* txt, int wdt) : content(wdg), name(txt), w(wdt) {}
	Widget* content;
	std::string name;
	int x, w;
};

struct TabsDataImp
{
	std::vector<TabsItemImp> tab;
};

Tabs::Tabs(const char* label, int w, int h, int x, int y, int tabHeight) : 
	WidgetSet(label, w, h, x, y), myData(0), myIsSelected(0), myTabsH(tabHeight), myIsMouseOverTab(-1)
{
	myData = new TabsDataImp;
}

Widget* Tabs::GetTab(int index)
{
	return myData->tab.at(index).content;
}

void Tabs::AddTab(const char* tabName, Widget* tab)
{
	myData->tab.push_back(TabsItemImp(tab, tabName, 0));
	Refit();
}

void Tabs::Tick(float deltaTime)
{
	myIsMouseOverTab = -1;
	for(int i=0; i<(int)myData->tab.size(); ++i)
	{
		const TabsItemImp& tab = myData->tab[i];
		if(mouse.IsInRect(myX+tab.x, myY, tab.w, myTabsH))
		{
			myIsMouseOverTab = i;
			if(mouse.IsClicked(Mouse::LMB)) myIsSelected = i;
		}
	}
	Widget* tabWidget = myData->tab[myIsSelected].content;
	if(tabWidget) tabWidget->Tick(deltaTime);
}

void Tabs::Draw()
{
	Draw::BarV(myX, myY, myX+myW, myY+myTabsH, myStyle.cBtm, myStyle.cTop);
	for(int i=0; i<(int)myData->tab.size(); ++i)
	{
		if(i == myIsSelected) continue;
		const TabsItemImp& tab = myData->tab[i];
		TmplFont.Print(tab.name.c_str(), myX+tab.x+tab.w/2, myY+myTabsH/2, myStyle.cTextDark, Font::CENTER, Font::CENTER);
		if(i < (int)myData->tab.size()-1)
		{
			Draw::LineV(myX+tab.x+tab.w, myY, myY+myTabsH, myStyle.cBtm);
		}
	}
	Draw::Box(myX, myY, myX+myW, myY+myTabsH, myStyle.cBorder);

	const TabsItemImp& tab = myData->tab[myIsSelected];
	const int tabW = (myIsSelected == (int)myData->tab.size()-1) ? myW - tab.x : tab.w;
	Draw::Box(myX+tab.x, myY, myX+tab.x+tabW, myY+myTabsH, myStyle.cBorder);
	Draw::BarV(myX+tab.x+1, myY+1, myX+tab.x+tabW-1, myY+myTabsH, myStyle.cTop, myStyle.cBtm);
	TmplFont.Print(tab.name.c_str(), myX+tab.x+tabW/2, myY+myTabsH/2, myStyle.cText, Font::CENTER, Font::CENTER);

	if(myIsMouseOverTab >= 0 && myIsMouseOverTab <= (int)myData->tab.size()-1)
	{
		const TabsItemImp& tab = myData->tab[myIsMouseOverTab];
		Draw::SetBlendMode(Draw::ADD);
		Draw::BarV(myX+tab.x+1, myY+1, myX+tab.x+tab.w-1, myY+myTabsH, 0xFF222222, 0xFF000000);
		Draw::SetBlendMode(Draw::NORMAL);
	}

	Widget* tabWidget = myData->tab[myIsSelected].content;
	if(tabWidget) tabWidget->Draw();
}

void Tabs::Refit()
{
	_CalcTabWidths();
	for(int i=0; i<(int)myData->tab.size(); ++i)
	{
		Widget* tab = myData->tab[i].content;
		if(tab)
		{
			tab->SetPos(myX, myY+myTabsH);
			tab->SetSize(myW, myH-myTabsH);
		}
	}
}

void Tabs::_CalcTabWidths()
{
	if(myData->tab.size() == 0) return;

	// Calculate total text width
	int textWidth = 0;
	for(int i=0; i<(int)myData->tab.size(); ++i)
	{
		textWidth += TmplFont.GetWidth(myData->tab[i].name.c_str());
	}
	int extraSpacePerTab = (myW - textWidth) / (int)myData->tab.size();

	// Assign width and position to tabs
	int tabX = 0;
	for(int i=0; i<(int)myData->tab.size(); ++i)
	{
		int txtw = TmplFont.GetWidth(myData->tab[i].name.c_str());
		myData->tab[i].w = txtw + extraSpacePerTab;
		myData->tab[i].x = tabX;
		tabX += txtw + extraSpacePerTab;
	}
}

}; // namespace tmpl